package logutil

import (
	"bytes"
	"fmt"
	"reflect"
	"regexp"
	"strconv"
	"strings"
)

const (
	Value_Nil = "<nil>"
)

var (
	allPH *regexp.Regexp
	vPH   *regexp.Regexp
)

func init() {
	allPH = regexp.MustCompile(`%[\+\-# 0]*[vTtbcdoqxXUeEfgGsp]{1}`)
	vPH = regexp.MustCompile(`v`)
}

type MyPrintf struct {
	alignment bool // 是否换行对齐
	buf       *bytes.Buffer
	filter    map[uintptr]bool
}

func Printf(v any, alignment bool) string {
	mf := &MyPrintf{
		alignment: alignment,
		buf:       &bytes.Buffer{},
		filter:    make(map[uintptr]bool),
	}

	mf.format(reflect.ValueOf(v), 0, true)

	return mf.buf.String()
}

func (p *MyPrintf) writeByte(b byte) {
	p.buf.WriteByte(b)
}

func (p *MyPrintf) writeString(str string) {
	p.buf.WriteString(str)
}

func (p *MyPrintf) writeAlignment(count int) {
	if !p.alignment {
		p.buf.WriteString("  ")
	} else {
		p.buf.WriteByte('\n')
		p.buf.WriteString(strings.Repeat("  ", count))
	}
}

func (p *MyPrintf) writeFormat(format string, v reflect.Value) {
	p.buf.WriteString(fmt.Sprintf(format, v))
}

func (p *MyPrintf) writeName(t reflect.Type) {
	switch t.Kind() {
	case reflect.Array:
		p.writeByte('[')
		p.writeString(strconv.FormatInt(int64(t.Len()), 10))
		p.writeByte(']')
		p.writeName(t.Elem())
	case reflect.Slice:
		p.writeString("[]")
		p.writeName(t.Elem())
	case reflect.Map:
		p.writeString("map[")
		p.writeName(t.Key())
		p.writeByte(']')
		p.writeName(t.Elem())
	case reflect.Pointer:
		p.writeByte('*')
		p.writeName(t.Elem())
	default:
		if pkg := t.PkgPath(); pkg != "" {
			p.writeString(pkg)
			p.writeByte('.')
		}

		if t.Name() != "" {
			p.writeString(t.Name())
		} else {
			p.writeString(t.String())
		}
	}
}

func (p *MyPrintf) format(v reflect.Value, depth int, needType bool) {
	switch v.Kind() {
	case reflect.Invalid:
		p.writeString(Value_Nil)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		p.format_Int(v, needType)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		p.format_Uint(v, needType)
	case reflect.Float32, reflect.Float64:
		p.format_Float(v, needType)
	case reflect.Complex64, reflect.Complex128:
		p.format_Complex(v, needType)
	case reflect.Bool:
		p.format_Bool(v, needType)
	case reflect.String:
		p.format_String(v, needType)
	case reflect.Chan, reflect.Func, reflect.UnsafePointer:
		p.format_Pointer1(v, needType)
	case reflect.Uintptr:
		p.format_Uintptr(v, needType)
	case reflect.Array:
		p.format_Array(v, depth, needType)
	case reflect.Slice:
		p.format_Slice(v, depth, needType)
	case reflect.Map:
		p.format_Map(v, depth, needType)
	case reflect.Struct:
		p.format_Struct(v, depth, needType)
	case reflect.Pointer:
		p.format_Pointer2(v, depth, needType)
	case reflect.Interface:
		p.format_Interface(v, depth)
	default:
		p.writeFormat("%#v", v)
	}
}

// 有符号整数
func (p *MyPrintf) format_Int(v reflect.Value, needType bool) {
	str := strconv.FormatInt(v.Int(), 10)
	if needType {
		p.writeString(v.Type().Name())
		p.writeByte('(')
		p.writeString(str)
		p.writeByte(')')
	} else {
		p.writeString(str)
	}
}

// 无符号整数
func (p *MyPrintf) format_Uint(v reflect.Value, needType bool) {
	str := strconv.FormatUint(v.Uint(), 10)

	if needType {
		p.writeString(v.Type().Name())
		p.writeByte('(')
		p.writeString(str)
		p.writeByte(')')
	} else {
		p.writeString(str)
	}
}

// 浮点数
func (p *MyPrintf) format_Float(v reflect.Value, needType bool) {
	bitSize := 64
	if v.Kind() == reflect.Float32 {
		bitSize = 32
	}
	str := strconv.FormatFloat(v.Float(), 'f', -1, bitSize)

	if needType {
		p.writeString(v.Type().Name())
		p.writeByte('(')
		p.writeString(str)
		p.writeByte(')')
	} else {
		p.writeString(str)
	}
}

// 复数
func (p *MyPrintf) format_Complex(v reflect.Value, needType bool) {
	str := strconv.FormatComplex(v.Complex(), 'f', -1, 128)

	if needType {
		p.writeString(v.Type().Name())
		// p.writeByte('(')
		p.writeString(str)
		// p.writeByte(')')
	} else {
		p.writeString(str)
	}
}

// 布尔值
func (p *MyPrintf) format_Bool(v reflect.Value, needType bool) {
	str := strconv.FormatBool(v.Bool())

	if needType {
		p.writeString(v.Type().Name())
		p.writeByte('(')
		p.writeString(str)
		p.writeByte(')')
	} else {
		p.writeString(str)
	}
}

// 字符串
func (p *MyPrintf) format_String(v reflect.Value, needType bool) {
	if needType {
		p.writeString(v.Type().String())
		p.writeByte('(')
		p.writeString(strconv.Quote(v.String()))
		p.writeByte(')')
	} else {
		p.writeString(strconv.Quote(v.String()))
	}
}

// 指针 不可往下追溯 如: chan func unsafePointer
func (p *MyPrintf) format_Pointer1(v reflect.Value, needType bool) {
	str := "0x" + strconv.FormatUint(uint64(v.Pointer()), 16)
	if needType {
		p.writeString(v.Type().String())
		p.writeByte('(')
		p.writeString(str)
		p.writeByte(')')
	} else {
		p.writeString(str)
	}
}

// 不带引用的指针值
func (p *MyPrintf) format_Uintptr(v reflect.Value, needType bool) {
	str := "0x" + strconv.FormatUint(v.Uint(), 16)
	if needType {
		p.writeString(v.Type().Name())
		p.writeByte('(')
		p.writeString(str)
		p.writeByte(')')
	} else {
		p.writeString(str)
	}
}

// 数组
func (p *MyPrintf) format_Array(v reflect.Value, depth int, needType bool) {
	if needType {
		p.writeName(v.Type())
	}

	elemType := false
	elem := v.Type().Elem()
	for elem.Kind() == reflect.Ptr {
		elem = elem.Elem()
	}
	if elem.Kind() == reflect.Interface {
		elemType = true
	}

	p.writeByte('[')
	for i := 0; i < v.Len(); i++ {
		p.writeAlignment(depth + 1)
		p.writeString(strconv.FormatInt(int64(i), 10))
		p.writeString(": ")
		p.format(v.Index(i), depth+1, elemType)
	}
	if v.Len() > 0 {
		p.writeAlignment(depth)
	}
	p.writeByte(']')
}

// 切片
func (p *MyPrintf) format_Slice(v reflect.Value, depth int, needType bool) {
	if needType {
		p.writeName(v.Type())
	}

	ptr := v.Pointer()
	p.writeString("(0x")
	p.writeString(strconv.FormatUint(uint64(ptr), 16))
	p.writeByte(')')

	if p.filter[ptr] {
		return
	}
	p.filter[ptr] = true

	elemType := false
	elem := v.Type().Elem()
	for elem.Kind() == reflect.Ptr {
		elem = elem.Elem()
	}
	if elem.Kind() == reflect.Interface {
		elemType = true
	}

	p.writeByte('[')
	for i := 0; i < v.Len(); i++ {
		p.writeAlignment(depth + 1)
		p.writeString(strconv.FormatInt(int64(i), 10))
		p.writeString(": ")
		p.format(v.Index(i), depth+1, elemType)
	}
	if v.Len() > 0 {
		p.writeAlignment(depth)
	}
	p.writeByte(']')
}

// Map
func (p *MyPrintf) format_Map(v reflect.Value, depth int, needType bool) {
	vType := v.Type()

	if needType {
		p.writeName(vType)
	}

	ptr := v.Pointer()
	p.writeString("(0x")
	p.writeString(strconv.FormatUint(uint64(ptr), 16))
	p.writeByte(')')

	if p.filter[ptr] {
		return
	}
	p.filter[ptr] = true

	keyType := false
	vKey := vType.Key()
	for vKey.Kind() == reflect.Pointer {
		vKey = vKey.Elem()
	}
	if vKey.Kind() == reflect.Interface {
		keyType = true
	}

	elemType := false
	elem := vType.Elem()
	for elem.Kind() == reflect.Pointer {
		elem = elem.Elem()
	}
	if elem.Kind() == reflect.Interface {
		elemType = true
	}

	mapKeys := v.MapKeys()
	p.writeByte('{')
	for _, key := range mapKeys {
		p.writeAlignment(depth + 1)
		p.format(key, depth+1, keyType)
		p.writeString(": ")
		p.format(v.MapIndex(key), depth+1, elemType)
	}
	if len(mapKeys) > 0 {
		p.writeAlignment(depth)
	}
	p.writeByte('}')
}

// 结构体
func (p *MyPrintf) format_Struct(v reflect.Value, depth int, needType bool) {
	if needType {
		p.writeName(v.Type())
	}
	p.writeByte('{')
	t := v.Type()
	for i := 0; i < t.NumField(); i++ {
		p.writeAlignment(depth + 1)
		p.writeString(t.Field(i).Name)
		p.writeString(": ")
		p.format(v.Field(i), depth+1, true)
	}
	if v.NumField() > 0 {
		p.writeAlignment(depth)
	}
	p.writeByte('}')
}

// 指针 可往下追溯
func (p *MyPrintf) format_Pointer2(v reflect.Value, depth int, needType bool) {
	if needType {
		p.writeByte('(')
		p.writeName(v.Type())
		p.writeByte(')')
	}

	if v.IsNil() {
		p.writeString(Value_Nil)
		return
	}

	ptr := v.Pointer()

	p.writeString("(0x")
	p.writeString(strconv.FormatUint(uint64(ptr), 16))
	p.writeByte(')')

	if p.filter[ptr] {
		return
	}
	p.filter[ptr] = true

	p.format(v.Elem(), depth, true)
}

// Interface
func (p *MyPrintf) format_Interface(v reflect.Value, depth int) {
	v = v.Elem()
	if v.IsValid() {
		p.format(v, depth, true)
	} else {
		p.writeString(Value_Nil)
	}
}
